跳到主要内容

2.1-TS的数据类型

Create by fall on 2021-02-15 Recently revised in 2022-04-19

简单类型的声明

Boolean

// 布尔类型
let flag:boolean = false

Number

和 JS 相同,TS 里面所有数字都是浮点数,都是 number

// 数字类型
let a1:number = 10 //十进制
let a2:number = 0b1010 //二进制
let a3:number = 0o12 // 八进制
let a4:number = 0xa // 十六进制
// 字符串类型
let str:string = '卷起千堆雪'

undefined & null

undefined 和 null可以作为其他数据类型的子类型(其他类型的子集),可以把该类型赋值给其他类型。

let notNum:undefined = undefined
let num1:number = null
let num2:number = undefined

Array

简单的声明

let arr1:number[] =[10,20,30,40] 
let arr2:Array<number> = [100,200,300]

特殊类型

any 类型

个数不确定,或者类型不确定都可以使用 any 进行定义

// 任意数据类型
let num1:any = '遇贵人先立业,遇佳人先成家,遇富婆成家立业'
let arr:any[]= [11,35,'年少不知软饭香,错把青春到插秧']

变量在声明的时候,没有初始化,并且没有指定类型,就会被识别为 any

解决 any 带来的问题,TypeScript 3.0 引入了 unknown 类型。

unknown

any,任何类型都可以赋值给 unknown,但是 unknown 不能赋值给除 any 外其他类型(除非使用断言)。

let cheat:unknown ='wo'
let lier:string = cheat

也就是说,如果不缩小类型,就不能进行任何操作,必须使用类型断言,或者是使用其他缩小类型的方法。

function getDogName() {
let x: unknown;
return x;
};
const dogName = getDogName();
// 直接使用
// const upName = dogName.toLowerCase(); // Error
// typeof
if (typeof dogName === 'string') {
const upName:string = dogName.toLowerCase(); // OK
}
// 类型断言
const upName = (dogName as string).toLowerCase(); // OK

void 类型

没有任何类型,在方法后面使用,意味着该函数没有返回值

function show():void{
console.log('富婆把我心儿碎,予我别墅和崩溃')
}

方法没有返回值时,我们需要定义成 void 而不是 undefined 否则会报错:

声明类型不为 voidany 的函数必须返回值。

object 类型

表示除了 number, string, boolean 之外的数据类型

function getObj(obj:object):object{
console.log(obj)
return {
name:'菲菲小公主',
age:62
}
}
console.log(getObj({giao:"giao"}))

never

表示一些永远不存在值的类型,有两种情况

  • 执行时抛出异常
  • 死循环代码,无限循环

never 是任何类型的子类型,也可以赋值给任何类型

function err(msg:string):never{
throw new Error(msg)
}
const err1 = (()=>{while(1){}})()

使用 never 避免出现新增了联合类型没有对应的实现,目的就是写出类型绝对安全的代码。

字面量类型

表示,子类型,比如

let str:'specific type' = 'specific type'
// 这段赋值表示:'specific type' 是 string 的子类型,而只能赋值为 string 的子类型 'specific type'
// 除此之外支持的字面量类型有
let num:666 = 666
let judge:true = true

字符串

单独的字面量类型没有多大意义,通过联合类型对数据进行限定

type Direction = 'forward' | 'backward'
function move():Direction{
if(Math.random()>0.5){
return 'forward'
}else{
return 'backward'
}
}

数字和布尔

interface State{
size:'big'|'middle'|'small'
edit:true|false
width: 120|180|240|360
}

元组 Tuple

元组类型允许表示一个已知元素数量和类型的数组,各元素的类型不必相同。 比如,你可以定义一对值分别为 stringnumber 类型的元组。

元组最重要的意义是限制数组元素的个数和类型。

let arr:[string,number,boolean,] = ['哈',233,true]
// 数组中每一个相应的类型都只能使用对应的方法
console.log(arr[0].substring(1)) // OK
// arr[1].substring(1) // Error, 'number' 不存在 'substring' 方法
// 解构赋值
let [param1,param2,param3]=arr
// 可选元素,可选元素后不能跟必填元素
let arr1:[number,boolean?] = [13]
// 剩余元素
let arr2:[string,...number[]]
// 只读元组
let arr3:readonly [number,string]=[996,520]

// 定义了 Combine 的类型
type Combine = number|number[]
const a:Combine = 996
const arr:Combine = [520,996]

枚举类型

枚举类型中,每一个数据都可以叫做元素,

可以为每一个元素进行编号,枚举中的数据值可以为中文,但是不推荐

enum Color{
cyan='#0ff',
magenta,
yellow
}
let color:Color = Color.red
console.log(Color.cyan+"---"+Color)

类型别名

详细可以查看 4.3 接口和类型别名

上面最后一个示例就用到了类型别名,使用 type 就可以声明类型的别名,懒惰是程序员的第一生产力嘛。

type Message = string|string[]
let handleString =(msg:Message)=>{
}
// 现在 Message 表示 string 类型,或是数组内为 string 的类型

接口

详细可以查看 4.3 接口和类型别名

用接口来表明定义对象的类型,必须按照所有已定义的类型进行存储,且不能有任何多余。

在实现接口时,使用 interface 进行定义对象的类型。

  • 可以使用 readonly 限制接口,只读
  • 也可以使用 ? 进行更改接口的值,使接口可以传值也可以不传。
interface OnePerson{
readonly name:string // readonly 表示该内容只读
age:number
hobby:string
salary?:number // ? 表示该内容可选
}
let tutou:OnePerson = {
name:'琦玉',
age:33,
hobby:"兴趣使然的英雄"
}
tutou.age = 66
console.log(tutou)

接口实现函数

interface Hero{
(name:string,heroName:string):boolean
}
const tutou:Hero = function(name:string,heroName:string):boolean{
// search 作用:返回符合元素的下标,如果为 -1 说明字符串中不存在该数值
return name.search(heroName) > -1
}
console.log(tutou("秃头披风侠","秃头"))

接口实现类

一个类的类型可以通过接口完成约束,并且一个类可以被多个接口进行约束。

接口和接口之间可以继承,类和接口之间称为实现

每一个方法必须都要可以使用

// 定义一个接口
interface OneHero{
hero()
}
// 此时接口可以看做 Person 类的一个约束
class Person implements OneHero{
hero(){
console.log('只有内心强大的人,才有资格做英雄')
}
}
let jodge = new Person
jodge.hero()
// 被多个接口修饰
interface TheHero{
action() // TheHero 限制必须拥有 action 方法
}
class Person implements OneHero,TheHero {
hero(){
console.log("一颗执着的心")
}
action(){
console.log("坚定不移的行动")
}
}
let faker = new Person
faker.hero()
faker.action()

交叉和联合

联合类型

取值时可以为多种类型中的一种类型,方法的两个类型(或者多个)都是可选的

function toString2(x: number | string) : string {
return x.toString()
}

联合类型数组

let arr:(number|string)[] = [12,'333']

联合类型常用用法

let num:1|2 = 1
type Events = 'click'|'scroll'|'mousemove'

交叉类型

交叉类型

// 一般是这么是用的
type Useless = number & string
// 但是也没啥意义,所以用下面的方法
type Format = string|number|number[]
type InitType = number|object
type Unity = Format&InitType
// 对象的联合
type InteractionType = {id:number,name:string}&{age:number}
const obj:InteractionType={
id:11,
name:'unknown',
age:555
}

确定类型

TS 如何确定,推断,或者是用户证明这个类型是什么类型

类型推断

在 TypeScript 中,具有初始化值的变量,有默认值得函数参数,函数返回类型都可以通过上下文判断出来

let a ='555'
a = 555 // 不能将 number 赋值给 string
// 推断返回类型
function add(a:number,b:number){
return a+ b
}
let sum = add(10,11)
sum = 'dssd'

断言

类型断言,有时候,你会比计算机更知道自己要怎么做,而此时,就可以使用断言

let arrayNum:number[] = [23,15,55,45]
// find 用于返回第一个满足条件的内容
// 你知道程序一定会返回数字,但是 TS 本身只是静态推断,因此它会认为可能返回 undefined
let num:number = arrayNum.find(num => num > 2) // ts 会报错
let num:number = arrayNum.find(num => num > 2) as number // 断言,认定返回的是 number 类型,此时赋值会忽略 undefined 的情况。

断言语法

// 尖括号 语法
const str:any = 'let me see it'
let number:number = (<string>str).length
// as 语法
const str2:any = 'jie no no no'
let number2:number = (str as string).length

因为尖括号语法,会和 JSX 冲突,所以建议使用 as 进行断言

非空断言

let superposition:number|null|undefined
superposition.toString()
superposition!.toString() // !放在声明变量末尾表示非空

确定赋值断言

如果一个数,没有被附初始值,就对它进行计算,会报错,但有一些情况 TS 无法捕捉,可以通过该方法帮助 TS 进行识别

let x:number
handle()
console.log(x*3) // error
function handle(){x=999}

let x!:number // 进行确定赋值断言,表示该值会被明确的赋值

谨慎使用断言 如果你没有按约定添加属性,TypeScript 编译器并不会对断言发出警告,所以使用时,要小心谨慎

interface Foo {
bar: number;
bas: string;
}
const foo = {} as Foo; // 它根本就不会报错
const fuu:Foo = {} // 报错

类型保护

类型保护和几个关键字有关 typeofinstanceofin

// typeof
function foo(x:string|number){
if(typeof x === 'string'){ // typescript 认定 x 的类型是 string
x.split('')
}
x.split('') // 此时 无法确定 x 的类型
}
// instanceof
interface Fee{
old:string
}
interface Cee{
young:number
}
function doStuff(man:Fee|Cee){
if(man instanceof Fee){
console.log(man.old) // OK
console.log(man.young) // Error
}
}
// in
interface A{
x:number
}
interface B{
y:number
}
function moveThem(dir:A|B){
if(x in dir){
// q:A
}else{
// q:B
}
}

字面量类型、定义的类型保护

type Foo = {
kind:'foo'
foo:number
}
type Bar = {
kind:'bar'
bar:string
}
function doStuff(arg:Foo|Bar){
if(arg.kind === 'foo'){
arg.foo // ok
arg.bar // Error
}else{
// 一定是 Bar
}
}

自定义类型保护

interface Foo {
foo: number;
common: string;
}
interface Bar {
bar: number;
common: string;
}
// 用户自己定义的类型保护!
function isFoo(arg: Foo | Bar): arg is Foo {
return (arg as Foo).foo !== undefined;
}
// 用户自己定义的类型保护使用用例:
function doStuff(arg: Foo | Bar) {
if (isFoo(arg)) {
console.log(arg.foo); // ok
console.log(arg.bar); // Error
} else {
console.log(arg.foo); // Error
console.log(arg.bar); // ok
}
}
doStuff({ foo: 123, common: '123' });
doStuff({ bar: 123, common: '123' });

注:这里只是最简单的使用,详情看下面的章节

类的基本使用

class SelfIntrduce {
name: string
age: number
hobby: string
constructor(
name: string = '小甜甜',
age: number = 19,
hobby: string = 'both'
) {
this.name = name
this.age = age
this.hobby = hobby
}
introduce() {
console.log(
`大家好,俺是${this.name},今年俺${this.age},俺喜欢${this.hobby}`
)
}
}
let liu = new SelfIntrduce('狗子', 3, '嘿嘿')
liu.introduce()

函数类型

注:这里只是最简单的使用,详情在 4.2 函数类型

函数类型的声明

function sum(x:number,y:number):number{
return x+y
}
let sum2:(x:number,y:number)=>number= (x,y) => x.toString()

泛型

详情请看 5.2 泛型

function random<T>(arg:T):T{
return arg
}
// 首先是 <T> 在调用函数时使用,T 将会被传入的类型取代。
random<string>('22')
random<number>(333)
// 就像传入参数一样,传入类型,然后调用类型

参考文章

作者链接
Jimmy_kiwihttps://juejin.cn/post/7018805943710253086
深入理解 TypeScripthttps://jkchao.github.io/typescript-book-chinese/